﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Xml;
using System.Xml.Linq;
using LynxWiki.Parser;
using LWinfrastructure;

namespace LynxWiki.DomainModel
{
    public class AttachmentVersion
    {
        public string AttachmentName { get; set; }
        public string AttachmentDir { get; set; }
        public string Namespace { get; set; }

        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm:ss}")]
        public DateTime CreatedOn { get; set; }
        public string Creator { get; set; }
        public int Version { get; set; }
        public string FileType { get; set; }
        public string CheckOutUser { get; set; }
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm:ss}")]
        public DateTime CheckedOutOn { get; set; }
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm:ss}")]
        public DateTime CheckedInOn { get; set; }
        private string _attachmentText;

        public string  AttachmentText
        {
            get
            {
                return _attachmentText;
            }
        }

        public bool CheckedOut
        {
            get
            {
                if (CheckedOutOn == null) {
                    return false;
                }
                else if (CheckedOutOn == DateTime.MinValue) {
                    return false;
                }
                else if (CheckedInOn == null) {
                    return true;
                }
                else if (CheckedInOn == DateTime.MinValue) {
                    return true;
                }
                else if (CheckedOutOn > CheckedInOn) {
                    return true;
                }
                return false;
            }       
        }

        public string CheckedOutOnString
        {
            get
            {
                if (CheckedOutOn == DateTime.MinValue) {
                    return string.Empty;
                } else {
                    return string.Format("{0:yyyy-MM-dd HH:mm:ss}", CheckedOutOn);
                }
            }
        }

        public string CheckedInOnString
        {
            get
            {
                if (CheckedInOn == DateTime.MinValue) {
                    return string.Empty;
                }
                else {
                    return string.Format("{0:yyyy-MM-dd HH:mm:ss}", CheckedInOn);
                }
            }
        }

        public async Task GetAttachmentText()
        {
            FileText fText = new FileText();
            HttpClient client = ClientUtility.GetIFilterClient();
            try {
                HttpResponseMessage response = await client.GetAsync("api/Values?filePath=" + AttachmentDir);
                response.EnsureSuccessStatusCode();

                fText = await response.Content.ReadAsAsync<FileText>();
                if (fText != null) {
                    _attachmentText = fText.TextData;
                }
            }
            catch (HttpRequestException ex) {
                Debug.Print(ex.Message);
            }
            catch (SerializationException ex) {
                Debug.Print(ex.Message);
            }
        }

        public string VersionFileName 
        {
            get {
                string name = AttachmentDir.Substring(0, AttachmentDir.LastIndexOf("."));
                string vers = string.Format("__v{0:0000}", Version);
                string ext = AttachmentDir.Substring(AttachmentDir.LastIndexOf("."));
                return name + vers + ext;
            }
        }

        public string FileUploadResult { get; set; }


        public List<string> Tags;
        public string VersionNote { get; set; }
        public List<string> DenyReadPrivsUser; //if empty then no deny; if a user or a group a match may not read or update (even if update open to all)
        public List<string> DenyReadPrivsRole; //if empty then no deny; if a user or a group a match may not read or update (even if update open to all)
        public List<string> AllowUpdatePrivsUser; //if empty then all may update (except those denied read), or may be limited to a number of users and/or groups
        public List<string> AllowUpdatePrivsRole; //if empty then all may update (except those denied read), or may be limited to a number of users and/or groups


        public AttachmentVersion()
        {
        }


        public AttachmentVersion(string name, string attachmentDir, string creator, int version, string fileType,
                string denyReadUser = "", string denyReadRole = "", string allowUpdateUser = "", string allowUpdateRole = "",
                string tags = "", string editRsn = "", string nspace = "")
        {
            this.AttachmentName = name;
            this.AttachmentDir = attachmentDir;
            this.Creator = creator;
            this.CreatedOn = DateTime.Now;
            this.Version = version;
            this.FileType = fileType;
            this.CheckedOutOn = DateTime.MinValue;
            this.CheckedInOn = DateTime.MinValue;
            this.Namespace = nspace;

            

            this.DenyReadPrivsUser = new List<string>();
            if (!string.IsNullOrEmpty(denyReadUser)) {
                string[] deny = denyReadUser.Split(',');
                if (deny.Count() > 0) {
                    foreach (string d in deny) {
                        DenyReadPrivsUser.Add(d.Trim());
                    }
                }
            }

            this.DenyReadPrivsRole = new List<string>();
            if (!string.IsNullOrEmpty(denyReadRole)) {
                string[] deny = denyReadRole.Split(',');
                if (deny.Count() > 0) {
                    foreach (string d in deny) {
                        DenyReadPrivsRole.Add(d.Trim());
                    }
                }
            }

            this.AllowUpdatePrivsUser = new List<string>();
            if (!string.IsNullOrEmpty(allowUpdateUser)) {
                string[] allow = allowUpdateUser.Split(',');
                if (allow.Count() > 0) {
                    foreach (string a in allow) {
                        AllowUpdatePrivsUser.Add(a.Trim());
                    }
                }
            }

            this.AllowUpdatePrivsRole = new List<string>();
            if (!string.IsNullOrEmpty(allowUpdateRole)) {
                string[] allow = allowUpdateRole.Split(',');
                if (allow.Count() > 0) {
                    foreach (string a in allow) {
                        AllowUpdatePrivsRole.Add(a.Trim());
                    }
                }
            }

            this.Tags = new List<string>();
            if (!string.IsNullOrEmpty(tags)) {
                string[] tag = tags.Split(',');
                if (tag.Count() > 0) {
                    foreach (string t in tag) {
                        Tags.Add(t.Trim());
                    }
                }
            }
            this.VersionNote = editRsn;
        }

        public AttachmentVersion(AttachmentVersion copy)
        {
            this.AttachmentDir = copy.AttachmentDir;
            this.Version = copy.Version;
            this.AttachmentName = copy.AttachmentName;
            this.VersionNote = copy.VersionNote;
            this.Creator = copy.Creator;
            this.CreatedOn = copy.CreatedOn;
            this.FileType = copy.FileType;
            this.CheckOutUser = copy.CheckOutUser;
            this.CheckedOutOn = copy.CheckedOutOn;
            this.CheckedInOn = copy.CheckedInOn;
            this.Tags = copy.Tags;
            this.AllowUpdatePrivsRole = copy.AllowUpdatePrivsRole;
            this.AllowUpdatePrivsUser = copy.AllowUpdatePrivsUser;
            this.DenyReadPrivsRole = copy.DenyReadPrivsRole;
            this.DenyReadPrivsUser = copy.DenyReadPrivsUser;
            this.Namespace = copy.Namespace;
            this._attachmentText = copy._attachmentText;
        }

        public bool UserWriteAccess(IPrincipal user)
        {
            if (AllowUpdatePrivsUser == null && AllowUpdatePrivsRole == null) {
                return true;
            }
            if (AllowUpdatePrivsUser != null && AllowUpdatePrivsUser.Count == 0
                && AllowUpdatePrivsRole != null && AllowUpdatePrivsRole.Count == 0) {
                return true;
            }
            if (AllowUpdatePrivsUser != null && AllowUpdatePrivsUser.Count > 0) {
                foreach (string aUser in AllowUpdatePrivsUser) {
                    if (aUser.ToLower() == user.Identity.Name.ToLower()) return true;
                }
            }
            if (AllowUpdatePrivsRole != null && AllowUpdatePrivsRole.Count > 0) {
                foreach (string aRole in AllowUpdatePrivsRole) {
                    if (user.IsInRole(aRole)) return true;
                }
            }
            return false;
        }

        public bool UserReadAccess(IPrincipal user)
        {
            if (user == null && (DenyReadPrivsUser != null || DenyReadPrivsRole != null)) return false;

            if (DenyReadPrivsUser == null && DenyReadPrivsRole == null) return true;

            if (user != null && user.Identity.GetType() == typeof(System.Security.Principal.GenericIdentity)) {
                if ((DenyReadPrivsUser != null && DenyReadPrivsUser.Count > 0) ||
                        (DenyReadPrivsRole != null && DenyReadPrivsRole.Count > 0)) {
                    return false;
                }
                else {
                    return true;
                }
            }

            if (DenyReadPrivsUser != null && DenyReadPrivsUser.Count == 0
                && DenyReadPrivsRole != null && DenyReadPrivsRole.Count == 0) {
                return true;
            }

            if (DenyReadPrivsUser != null && DenyReadPrivsUser.Count > 0) {
                foreach (string dUser in DenyReadPrivsUser) {
                    if (dUser.ToLower() == user.Identity.Name.ToLower()) return false;
                }
            }

            if (DenyReadPrivsRole != null && DenyReadPrivsRole.Count > 0) {
                foreach (string dRole in DenyReadPrivsRole) {
                    if (user.IsInRole(dRole)) return false;
                }
            }

            return true;
        }


        public AttachmentVersion(string xml)
        {
            XDocument doc = XDocument.Parse(xml);
            AttachmentVersion temp = (from item in doc.Descendants("attachmentVersion")
                                 select new AttachmentVersion {
                                     AttachmentName = (string)item.Element("attachmentName"),
                                     AttachmentDir = (string)item.Element("attachmentDir"),
                                     Creator = (string)item.Element("creator"),
                                     CreatedOn = (DateTime)item.Element("createdOn"),
                                     Version = (int)item.Element("version"),
                                     FileType = (string)item.Element("fileType"),
                                     CheckOutUser = (string)item.Element("checkOutUser"),
                                     CheckedOutOn = (DateTime)item.Element("checkedOutOn"),
                                     CheckedInOn = (DateTime)item.Element("checkedInOn"),
                                     VersionNote = (string)item.Element("versionNote"),
                                     Namespace = (string)item.Element("nameSpace"),
                                     _attachmentText = (string)item.Element("attachmentText")
                                 }).First();
            List<string> tempTags = doc.Descendants("attachmentVersion").Elements("tags").Select(t => (string)t.Attribute("id")).ToList();
            List<string> tempDenyUser = doc.Descendants("attachmentVersion").Elements("denyUser").Select(t => (string)t.Attribute("id")).ToList();
            List<string> tempDenyRole = doc.Descendants("attachmentVersion").Elements("denyRole").Select(t => (string)t.Attribute("id")).ToList();
            List<string> tempAllowUser = doc.Descendants("attachmentVersion").Elements("allowUser").Select(t => (string)t.Attribute("id")).ToList();
            List<string> tempAllowRole = doc.Descendants("attachmentVersion").Elements("allowRole").Select(t => (string)t.Attribute("id")).ToList();
            this.AttachmentName = temp.AttachmentName;
            this.AttachmentDir = temp.AttachmentDir;
            this.Creator = temp.Creator;
            this.CreatedOn = temp.CreatedOn;
            this.Version = temp.Version;
            this.FileType = temp.FileType;
            this.CheckOutUser = temp.CheckOutUser;
            this.CheckedOutOn = temp.CheckedOutOn;
            this.CheckedInOn = temp.CheckedInOn;
            this.VersionNote = temp.VersionNote;
            this.Namespace = temp.Namespace;
            this._attachmentText = temp._attachmentText;
            this.Tags = tempTags;
            this.DenyReadPrivsUser = tempDenyUser;
            this.DenyReadPrivsRole = tempDenyRole;
            this.AllowUpdatePrivsUser = tempAllowUser;
            this.AllowUpdatePrivsRole = tempAllowRole;
        }

        public string ToXml()
        {
            XDocument doc = new XDocument(
                new XElement("attachmentVersion",
                    new XElement("attachmentName", AttachmentName),
                    new XElement("attachmentDir", AttachmentDir),
                    new XElement("creator", Creator),
                    new XElement("createdOn", CreatedOn.ToString("yyyy-MM-ddTHH:mm:ss.fff")),
                    new XElement("version", Version.ToString()),
                    new XElement("fileType", FileType),
                    new XElement("checkOutUser", CheckOutUser),
                    new XElement("checkedOutOn", CheckedOutOn.ToString("yyyy-MM-ddTHH:mm:ss.fff")),
                    new XElement("checkedInOn", CheckedInOn.ToString("yyyy-MM-ddTHH:mm:ss.fff")),
                    Tags.Select(tag => new XElement("tags", new XAttribute("id", tag))),
                    new XElement("versionNote", VersionNote),
                    new XElement("nameSpace", Namespace),
                    new XElement("attachmentText", AttachmentText),
                    AllowUpdatePrivsUser.Select(allowUser => new XElement("allowUser", new XAttribute("id", allowUser))),
                    AllowUpdatePrivsRole.Select(allowRole => new XElement("allowRole", new XAttribute("id", allowRole))),
                    DenyReadPrivsUser.Select(denyUser => new XElement("denyUser", new XAttribute("id", denyUser))),
                    DenyReadPrivsRole.Select(denyRole => new XElement("denyRole", new XAttribute("id", denyRole)))
                    ));
            return doc.ToString(SaveOptions.None);
        }

        public void CheckOut(string checkOutUser)
        {
            this.CheckOutUser = checkOutUser;
            this.CheckedOutOn = DateTime.Now;
        }

        public void CheckIn()
        {
            this.CheckedInOn = DateTime.Now;
        }

        public bool Updatable(IPrincipal user)
        {
            if (user.Identity.GetType() == typeof(System.Security.Principal.GenericIdentity)) {
                if (AllowUpdatePrivsUser != null && AllowUpdatePrivsUser.Count > 0) return false;
                if (AllowUpdatePrivsRole != null && AllowUpdatePrivsRole.Count > 0) return false;
                if (this.CheckedOutOn == DateTime.MinValue) return true;
                else if (this.CheckedInOn > this.CheckedOutOn) return true;
                else
                    return false;
            }
            else if (UserWriteAccess(user)) {
                if (this.CheckedOutOn == DateTime.MinValue) return true;
                else if (this.CheckedInOn > this.CheckedOutOn) return true;
                else
                    return false;
            }
            return false;
        }

    }
}
